package org.summer.lawyer.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Service;
import org.summer.common.core.exception.ServiceException;
import org.summer.common.core.utils.MapstructUtils;
import org.summer.common.json.utils.JsonUtils;
import org.summer.common.mybatis.core.page.PageQuery;
import org.summer.common.mybatis.core.page.TableDataInfo;
import org.summer.common.satoken.utils.LoginHelper;
import org.summer.lawyer.constant.SystemConstants;
import org.summer.lawyer.constant.enums.PayTransferStatus;
import org.summer.lawyer.constant.enums.PaymentMethod;
import org.summer.lawyer.constant.enums.WithdrawStatus;
import org.summer.lawyer.domain.PayTransfer;
import org.summer.lawyer.domain.bo.paytransfer.PayTransferBo;
import org.summer.lawyer.domain.vo.payapp.PayAppVo;
import org.summer.lawyer.domain.vo.paychannel.PayChannelVo;
import org.summer.lawyer.domain.vo.paytransfer.PayTransferVo;
import org.summer.lawyer.domain.vo.userwithdraw.UserWithdrawVo;
import org.summer.lawyer.event.PayTransferSuccessEvent;
import org.summer.lawyer.mapper.PayTransferMapper;
import org.summer.lawyer.service.*;
import org.summer.lawyer.util.IdWorkerUtil;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 转账单Service业务层处理
 *
 * @author summer
 * @date 2024-06-23
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PayTransferServiceImpl extends ServiceImpl<PayTransferMapper, PayTransfer> implements IPayTransferService, ApplicationEventPublisherAware {

    private final IPayAppService payAppService;

    private final IPayChannelService payChannelService;

    private final IUserWithdrawService userWithdrawService;

    private final IBrokerageUserService brokerageUserService;

    private final IUserWalletService userWalletService;

    /**
     * 查询转账单
     *
     * @param id 主键
     * @return 转账单
     */
    @Override
    public PayTransferVo selectPayTransferVoById(Long id) {
        return baseMapper.selectVoById(id);
    }

    @Override
    public PayTransferVo selectPayTransferVoByTransferNo(String transferNo) {
        LambdaQueryWrapper<PayTransfer> lqw = Wrappers.lambdaQuery();
        lqw.eq(PayTransfer::getTransferNo, transferNo);
        return baseMapper.selectVoOne(lqw, false);
    }

    @Override
    public PayTransferVo selectPayTransferVoByTransferBatchNo(String transferBatchNo) {
        LambdaQueryWrapper<PayTransfer> lqw = Wrappers.lambdaQuery();
        lqw.eq(PayTransfer::getTransferBatchNo, transferBatchNo);
        return baseMapper.selectVoOne(lqw, false);
    }

    /**
     * 分页查询转账单列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 转账单分页列表
     */
    @Override
    public TableDataInfo<PayTransferVo> selectPageList(PayTransferBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<PayTransfer> lqw = buildQueryWrapper(bo);
        Page<PayTransferVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的转账单列表
     *
     * @param bo 查询条件
     * @return 转账单列表
     */
    @Override
    public List<PayTransferVo> selectPayTransferVoList(PayTransferBo bo) {
        LambdaQueryWrapper<PayTransfer> lqw = Wrappers.lambdaQuery();
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<PayTransfer> buildQueryWrapper(PayTransferBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<PayTransfer> lqw = Wrappers.lambdaQuery();
        lqw.eq(StrUtil.isNotBlank(bo.getTransferNo()), PayTransfer::getTransferNo, bo.getTransferNo());
        lqw.eq(bo.getAppId() != null, PayTransfer::getAppId, bo.getAppId());
        lqw.eq(StrUtil.isNotBlank(bo.getWxAppId()), PayTransfer::getWxAppId, bo.getWxAppId());
        lqw.eq(StrUtil.isNotBlank(bo.getMchNo()), PayTransfer::getMchNo, bo.getMchNo());
        lqw.like(StrUtil.isNotBlank(bo.getMchName()), PayTransfer::getMchName, bo.getMchName());
        lqw.eq(bo.getChannelId() != null, PayTransfer::getChannelId, bo.getChannelId());
        lqw.eq(StrUtil.isNotBlank(bo.getChannelCode()), PayTransfer::getChannelCode, bo.getChannelCode());
        lqw.eq(bo.getWithdrawNumber() != null, PayTransfer::getWithdrawNumber, bo.getWithdrawNumber());
        lqw.eq(bo.getUserId() != null, PayTransfer::getUserId, bo.getUserId());
        lqw.eq(StrUtil.isNotBlank(bo.getOpenId()), PayTransfer::getOpenId, bo.getOpenId());
        lqw.eq(StrUtil.isNotBlank(bo.getTradeNo()), PayTransfer::getTradeNo, bo.getTradeNo());
        lqw.eq(bo.getPayWay() != null, PayTransfer::getPayWay, bo.getPayWay());
        lqw.eq(bo.getTransferStatus() != null, PayTransfer::getTransferStatus, bo.getTransferStatus());
        lqw.eq(bo.getReturnStatus() != null, PayTransfer::getReturnStatus, bo.getReturnStatus());
        lqw.eq(bo.getAmount() != null, PayTransfer::getAmount, bo.getAmount());
        lqw.eq(StrUtil.isNotBlank(bo.getResultCode()), PayTransfer::getResultCode, bo.getResultCode());
        lqw.eq(StrUtil.isNotBlank(bo.getResultMsg()), PayTransfer::getResultMsg, bo.getResultMsg());
        lqw.eq(StrUtil.isNotBlank(bo.getPostJson()), PayTransfer::getPostJson, bo.getPostJson());
        lqw.eq(StrUtil.isNotBlank(bo.getReturnJson()), PayTransfer::getReturnJson, bo.getReturnJson());
        lqw.eq(bo.getReturnTime() != null, PayTransfer::getReturnTime, bo.getReturnTime());
        lqw.eq(StrUtil.isNotBlank(bo.getNotifyUrl()), PayTransfer::getNotifyUrl, bo.getNotifyUrl());
        lqw.eq(bo.getSuccessTime() != null, PayTransfer::getSuccessTime, bo.getSuccessTime());
        return lqw;
    }

    /**
     * 新增转账单
     *
     * @param bo 转账单
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(PayTransferBo bo) {
        PayTransfer add = MapstructUtils.convert(bo, PayTransfer.class);
        validEntityBeforeSave(add);
        add.setTransferStatus(0);
        add.setCreateTime(DateUtil.date());
        add.setUpdateTime(DateUtil.date());
        if (LoginHelper.getLoginUser() != null) {
            add.setCreateBy(LoginHelper.getLoginUser().getUserId());
            add.setUpdateBy(LoginHelper.getLoginUser().getUserId());
        }
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改转账单
     *
     * @param bo 转账单
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(PayTransferBo bo) {
        PayTransfer update = MapstructUtils.convert(bo, PayTransfer.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(PayTransfer entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除转账单信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Lock4j(keys = "{#bo.withdrawNumber}", acquireTimeout = 1000, expire = 10000)
    @DSTransactional(rollbackFor = Exception.class)
    @Override
    public PayTransferVo submitTransferOrder(PayTransferBo bo) {
        //查询提现记录
        UserWithdrawVo userWithdrawVo = userWithdrawService.selectUserWithdrawVoByWithdrawNumber(bo.getWithdrawNumber());

        if (!WithdrawStatus.PENDING.getCode().equals(userWithdrawVo.getWithdrawStatus()) && !WithdrawStatus.FAILURE.getCode().equals(userWithdrawVo.getWithdrawStatus())) {
            throw new ServiceException(StrUtil.format("用户 {} 的提现正在处理中，请勿重复提交", userWithdrawVo.getRealName()));
        }
        PayChannelVo channelVo = payChannelService.selectPayChannelVoByCode(PaymentMethod.WECHAT_MINIAPP.getCode(), userWithdrawVo.getAppId());
        if (ObjectUtil.isNull(channelVo) || SystemConstants.INACTIVE.equals(channelVo.getStatus())) {
            throw new ServiceException("请先配置支付通道");
        }
        PayAppVo payAppVo = payAppService.selectPayAppVoById(channelVo.getAppId());
        if (Objects.isNull(payAppVo)) {
            throw new ServiceException("请先配置支付应用");
        }
        PayTransferBo payTransferBo = new PayTransferBo();
        payTransferBo.setTransferBatchNo(IdWorkerUtil.nextIdStr());
        payTransferBo.setTransferNo(IdWorkerUtil.nextIdStr());
        payTransferBo.setAppId(payAppVo.getAppId());
        payTransferBo.setWxAppId(channelVo.getWxAppId());
        payTransferBo.setMchNo(channelVo.getMchNo());
        payTransferBo.setMchName(channelVo.getMchName());
        payTransferBo.setChannelCode(channelVo.getChannelCode());
        payTransferBo.setChannelId(channelVo.getId());
        payTransferBo.setWithdrawNumber(userWithdrawVo.getWithdrawNumber());
        payTransferBo.setUserId(userWithdrawVo.getWxUserId());
        payTransferBo.setOpenId(userWithdrawVo.getReceivableAccount());
        payTransferBo.setUserName(userWithdrawVo.getRealName());
        payTransferBo.setTransferRemark(StrUtil.format("提现单号：{}", userWithdrawVo.getWithdrawNumber()));
        payTransferBo.setPayWay(PaymentMethod.WECHAT_MINIAPP.getCode());
        payTransferBo.setTransferStatus(PayTransferStatus.ACCEPTED.getCode());
        payTransferBo.setTransferTime(DateUtil.date());
        payTransferBo.setReturnStatus(0);
        payTransferBo.setAmount(userWithdrawVo.getPayment());
        payTransferBo.setNotifyUrl(StrUtil.format("{}/{}", payAppVo.getTransferNotifyUrl(), channelVo.getId()));
        payTransferBo.setPostJson(JsonUtils.toJsonString(payTransferBo));
        //提交转账
     //   PayClient client = payChannelService.getPayClient(channelVo.getId());
    //    PayTransferVo transferVo = client.unifiedTransfer(payTransferBo);
        // 4. 如果调用直接转账成功，则直接更新支付单状态为成功。例如说：付款码支付，免密支付时，就直接验证支付成功
        //2024-08-31 修改（取消自动打款，用户自己微信付款）
        PayTransferVo transferVo = new PayTransferVo();
        transferVo.setTransferNo(payTransferBo.getTransferNo());
        transferVo.setTransferBatchNo(payTransferBo.getTransferBatchNo());
        transferVo.setTransferStatus(PayTransferStatus.SUCCESS.getCode());
        transferVo.setTransferTime(DateUtil.date());
        transferVo.setSuccessTime(DateUtil.toLocalDateTime(DateUtil.date()));
        transferVo.setReturnJson("");
        transferVo.setResultMsg("");
        transferVo.setResultCode("");
        //修改结束

        payTransferBo.setTransferStatus(transferVo.getTransferStatus());
        payTransferBo.setTradeNo(transferVo.getTradeNo());
        payTransferBo.setResultCode(transferVo.getResultCode());
        payTransferBo.setResultMsg(transferVo.getResultMsg());
        payTransferBo.setReturnJson(transferVo.getReturnJson());
        payTransferBo.setSuccessTime(transferVo.getSuccessTime());
        if (PayTransferStatus.SUCCESS.getCode().equals(transferVo.getTransferStatus())) {
            //直接转账成功
            payTransferBo.setReturnStatus(1);
            payTransferNotify(transferVo);
        }
        this.insertByBo(bo);
        if (PayTransferStatus.FAIL.getCode().equals(transferVo.getTransferStatus())) {
            log.error("[{}]转账失败，{}，数据：{}", payTransferBo.getWithdrawNumber(), transferVo.getResultMsg(), JsonUtils.toJsonString(payTransferBo));
            throw new ServiceException(transferVo.getResultMsg());
        }
        return selectPayTransferVoById(bo.getId());
    }

    @DSTransactional(rollbackFor = Exception.class)
    @Override
    public void payTransferNotify(PayTransferVo vo) {
        PayTransferVo payTransferVo = selectPayTransferVoByTransferBatchNo(vo.getTransferBatchNo());
        if (payTransferVo == null) {
            log.error("[{}]转账单信息未找到，回调信息：{}", vo.getTransferBatchNo(), JsonUtils.toJsonString(vo));
            throw new ServiceException(StrUtil.format("[{}]转账单信息未找到", vo.getTransferBatchNo()));
        }
        if (!PayTransferStatus.SUCCESS.getCode().equals(payTransferVo.getTransferStatus()) && PayTransferStatus.SUCCESS.getCode().equals(vo.getTransferStatus())) {
            PayTransfer payTransfer = new PayTransfer();
            payTransfer.setId(payTransferVo.getId());
            payTransfer.setTransferStatus(vo.getTransferStatus());
            payTransfer.setReturnTime(DateUtil.date());
            payTransfer.setResultCode(vo.getResultCode());
            payTransfer.setResultMsg(vo.getResultMsg());
            payTransfer.setReturnJson(vo.getReturnJson());
            payTransfer.setSuccessTime(vo.getSuccessTime());
            payTransfer.setUpdateTime(DateUtil.date());
            LambdaUpdateWrapper<PayTransfer> updateWrapper = Wrappers.lambdaUpdate();
            updateWrapper.eq(PayTransfer::getId, payTransferVo.getId());
            baseMapper.update(payTransfer, updateWrapper);
            payTransferVo = selectPayTransferVoByTransferBatchNo(vo.getTransferBatchNo());
            //处理佣金记录
            brokerageUserService.updateBrokerageWithdraw(payTransferVo);
            //处理用户钱包
            userWalletService.walletExpense(payTransferVo);
            //推送事件
            applicationEventPublisher.publishEvent(new PayTransferSuccessEvent(this, payTransferVo));
        }

    }

    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}
